package org.hackystat.devcathlon.engine.profile;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hackystat.devcathlon.engine.user.User;
import org.hackystat.devcathlon.engine.db.util.HibernateUtil;
import org.hackystat.devcathlon.engine.user.UserManager;

/**
 * A thread-safe singleton class that manages access to user-profiles. 
 * @author anthony.m.du
 */
public class ProfileManager {

  /** The singleton instance. */
  private static ProfileManager theInstance = new ProfileManager();
  /** The (user name, profile instance) tuples. */
  private Map<String, Profile> emails2profiles = new HashMap<String, Profile>();

  /** The private constructor. */
  private ProfileManager() {
    // does nothing at this point; will initialize from database later.
  }

  /**
   * Returns the singleton instance of the ProfileManager.
   * @return The ProfileManager.
   */
  public static ProfileManager getInstance() {
    return ProfileManager.theInstance;
  }

  /**
   * Gets a list of all Profiles in the system.
   * @return The list of Profiles.
   */
  public synchronized List getProfiles() {
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    String hqlSelect = "from Profile";
    List list = null;
    try {
      session.beginTransaction();
      Query query = session.createQuery(hqlSelect);
      list = query.list();
      session.getTransaction().commit();
    }
    catch (HibernateException ex) {
      Transaction tx = session.getTransaction();
      if (tx.isActive()) {
        tx.rollback();
      }
      throw ex;
    }
    return list;
  }

  /**
   * Adds a new user profile to the system. 
   * @param user The user's Hackystat account.
   * @param gender The user's gender.
   * @param location The user's location.
   * @param contact The user's contact.
   * @param bioInfo The user's bioInfo.
   * @param avatar The user's avatar.
   * @return The new User instance, if successful.
   * @throws ProfileException If problems occur creating the user. 
   */
  public synchronized Profile addProfile(User user,
          String gender, String location, String contact,
          String bioInfo, String avatar)
          throws ProfileException {
    String email = user.getEmail();
    String firstName = user.getFirstName();
    String lastName = user.getLastName();
    /*
    if (this.emails2profiles.containsKey(email)) {
    throw new ProfileException("Attempt to add Profile with duplicate email: " + email);
    }*/
    Profile profile = null;
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    try {
      session.beginTransaction();
      // Also create a profile. Profile and User classes are synonymous.
      profile = new Profile();
      profile.setScreenName(email);
      profile.setFirstName(firstName);
      profile.setLastName(lastName);
      profile.setGender(gender);
      profile.setContact(contact);
      profile.setLocation(location);
      profile.setBioInfo(bioInfo);
      profile.setAvatar(avatar);
      profile.addEmail(email);
      profile.setUser(user);
      user.setProfile(profile);
      this.emails2profiles.put(email, profile);
      session.save(profile);
      session.update(user);
      System.out.println("Created Profile: " + profile.getScreenName());
      session.getTransaction().commit();
    }
    catch (HibernateException ex) {
      Transaction tx = session.getTransaction();
      if (tx.isActive()) {
        tx.rollback();
      }
      throw ex;
    }
    return profile;
  }

  /**
   * Returns the Profile instance associated with the email, or null if not found.
   * @param email The email of the Profile of interest.
   * @return The Profile instance, or null if not found.
   */
  public synchronized Profile getProfile(String email) {
//    return this.emails2profiles.get(email);
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    Profile profile = null;
    try {
      session.beginTransaction();
      String hqlSelect = "from User u where u.email = :email";
      Query query = session.createQuery(hqlSelect).setString("email", email);
      query.setMaxResults(1);
      User user = (User) query.uniqueResult();
      profile = user.getProfile();
      session.getTransaction().commit();
    }
    catch (HibernateException ex) {
      throw ex;
    }
    return profile;
  }

  /**
   * Removes the specified user from the map of users. 
   * Does nothing if this user is not currently on the map.
   * @param email The email of the user to remove. 
   */
  public synchronized void deleteProfile(String email) {
    this.emails2profiles.remove(email);
    User user = UserManager.getInstance().getUser(email);
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    session.beginTransaction();
    if (user != null && null != user.getProfile()) {
      session.delete(user.getProfile());
    }
//    String hqlSelect = "from User u where u.email = :email";
//    Query query = session.createQuery(hqlSelect).setString("email", email);
//    query.setMaxResults(1);
//    User user = (User) query.uniqueResult();
//    String hqlDelete = "delete Profile p where p.id = :user_id";
//    int row = session.createQuery(hqlDelete).setLong("user_id", user.getId()).executeUpdate();
//    System.out.println("Rows affected: " + row);
    session.getTransaction().commit();
  }

  /**
   * Retrieves all profiles in the ProfileManager.
   * @return The list containing all profiles.
   */
  @SuppressWarnings("unchecked")
  public synchronized List<Profile> getAllProfiles() {
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    List<Profile> profiles = new ArrayList<Profile>();
    try {
      session.beginTransaction();
      String hqlSelect = "from Profile";
      Query query = session.createQuery(hqlSelect);
      profiles = query.list();
      session.getTransaction().commit();
    }
    catch (HibernateException e) {
      Transaction tx = session.getTransaction();
      if (tx.isActive()) {
        tx.rollback();
      }
    }
    return profiles;
//     return new ArrayList<Profile>(this.emails2profiles.values());
  }

  /**
   * Adds email addresses to the given user-profile.
   * @param profileId The profile id.
   * @param emailAddress The email address to add.
   */
  @SuppressWarnings("unused")
  private synchronized void addEmailToProfile(Long profileId, String emailAddress) {
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    session.beginTransaction();
    Profile profile = (Profile) session.load(Profile.class, profileId);
    // The getEmailAddresses() might trigger a lazy load of the collection
    profile.getEmailAddresses().add(emailAddress);
    session.getTransaction().commit();
  }
}
